#include <stdio.h>
#include <string.h>	/* for using strtok, strcmp, etc */
#include <strings.h>    /* for bcopy, bzero */
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <netdb.h>

#include "common.h"

#define pAssert(a,b) {if (!(a)) {printf("Line %d in File %s: %s\n", __LINE__, __FILE__, b); exit(1);}}
#define pError(e,a) {if (e) {perror(a); exit(1);}}

int main (int argc, char **argv)
{
  char  client_msg[MAX_STR_SIZE];
  char  server_msg1[MAX_STR_SIZE];
  char  server_msg2[MAX_STR_SIZE];
  int   sd;
  struct sockaddr_in sin;
  struct hostent *h;

  short port;
  char  magic_str[] = MAGIC_STRING;

  char delims[] = "\t \n";	/* delimiters */
  char *token = 0x0, *addr_string = 0x0, *rand_num_str = 0x0, *rand_num_str2 = 0x0;

  /* Verify command-line arguments */
  if (argc < 2 || argc > 4) {
    printf("usage: %s [<hostname>[ <port>]] <last name>\n", argv[0]);
    exit(1);
  }

  h = (argc > 2) ? gethostbyname(argv[1]) : gethostbyname(SERVER_HOSTNAME);

  pAssert(h, "gethostbyname failed\n");

  port = (argc == 4) ? atoi(argv[2]) : SERVER_PORT;

  /* Allocate a socket (type SOCK_STREAM for TCP) */
  sd = socket(PF_INET, SOCK_STREAM, 0);
  pError((sd==-1), "socket");

  /* Fill in '<struct sockaddr_in sin>' */
  bzero((char *) &sin, sizeof(sin));
  sin.sin_family = PF_INET; 
  bcopy(h->h_addr, (char *)&sin.sin_addr, h->h_length);
  sin.sin_port = htons(port); 

  /* Connect to the server */
  int rCode = connect(sd, (struct sockaddr *)&sin, sizeof(sin));
  char err_msg[MAX_STR_SIZE];
  sprintf(err_msg, "ERROR: Connection to server failed! Return code: %d", rCode);
  pAssert(rCode == 0, err_msg);

  /* Format <client_msg> with HELLO string */
  sprintf(client_msg, "%s HELLO %s\n", magic_str, argv[argc-1]);
  printf("%s", client_msg);

  // Send client_msg to server
  send(sd, client_msg, MAX_STR_SIZE, 0);

  /* Wait for server's STATUS message, read it into server_msg1 */
  recv(sd, server_msg1, MAX_STR_SIZE, 0);
  printf("%s", server_msg1);

  /* server_msg1 contains the string back from the server */ 
  token = strtok(server_msg1, delims);
  pAssert(token && !strcmp(token, magic_str), "Invalid Magic String");

  token = strtok(NULL, delims);
  pAssert(token && !strcmp(token, "STATUS"), "Invalid Message Type");

  token = strtok(NULL, delims);
  pAssert(token, "Random number");

  rand_num_str = token;

  token = strtok(NULL, delims);
  pAssert(token, "Random number");

  rand_num_str2 = token;

  token = strtok(NULL, delims);
  pAssert(token, "Invalid IP Address string");

  addr_string = token;

  token = strtok(NULL, delims);
  pAssert(!strtok(NULL, delims), "Too many tokens");

  sprintf(client_msg,"%s BYE %d %s\n", magic_str, atoi(rand_num_str) + atoi(rand_num_str2), "Hasta La Vista, Baby");
  printf("%s", client_msg);
  // Send client_msg to server
  send(sd, client_msg, MAX_STR_SIZE, 0);

  /* Wait for server's CONFIRM_BYE message, put it into <server_msg2> */
  recv(sd, server_msg2, MAX_STR_SIZE, 0);
  printf("%s", server_msg2);

  token = strtok(server_msg2, delims);
  pAssert(token && !strcmp(token, magic_str), "Invalid Magic String");

  token = strtok(NULL, delims);
  pAssert(token && !strcmp(token, "CONFIRM_BYE"), "Invalid Message Type");

  token = strtok(NULL, delims);
  pAssert(!strtok(NULL, delims), "Too many tokens");

  /* close the socket and clean up */
  close(sd);

  return 0;
}
